import 'dart:async';
import 'dart:io';

import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:gim_plugin/gim_plugin.dart';
import 'package:gim_plugin_example/sdk/gim_sdk.dart';
import 'package:gim_plugin_example/utils/logger_utils.dart';
import 'package:gim_plugin_example/utils/permission_utils.dart';
import 'package:gim_plugin_example/utils/toast_utils.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';

import '../../../std.dart';
import '../pg_chat_room_store.dart';

class SendVoice extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _SendVoice();
}

class _SendVoice extends State<SendVoice> {
  FlutterSoundRecorder recorder = FlutterSoundRecorder();
  EventBus eventBus = EventBus();
  StreamSubscription? recorderSubscription;
  late _EvtVoiceVolumePool evtPool;
  final double maxDy = 70;
  bool recording = false;
  double dy = 0;

  String? path;
  int startTimestamp = 0;
  int endTimestamp = 0;

  @override
  void initState() {
    evtPool = _EvtVoiceVolumePool(eventBus);
    init();
    super.initState();
  }

  Future<void> init() async {
    await recorder.openAudioSession(
        focus: AudioFocus.requestFocusTransient,
        category: SessionCategory.playAndRecord,
        mode: SessionMode.modeDefault,
        device: AudioDevice.speaker);
    await recorder.setSubscriptionDuration(const Duration(milliseconds: 60));
  }

  @override
  void dispose() {
    cancelRecorderSubscriptions();
    recorder.closeAudioSession();
    eventBus.destroy();
    super.dispose();
  }

  cancelRecorderSubscriptions() {
    recorderSubscription?.cancel();
    recorderSubscription = null;
  }

  Future<void> startRecord() async {
    if (recording || !await applyMicrophonePermission(context:context)) return;
    recording = true;
    setState(() {});
    //开始录音
    try {
      Directory tempDir = await getTemporaryDirectory();
      var time = DateTime.now().millisecondsSinceEpoch ~/ 1000;
      path = '${tempDir.path}/$time${ext[Codec.aacADTS.index]}';
      await recorder.startRecorder(
        toFile: path,
        codec: Codec.aacADTS,
      );
      startTimestamp = DateTime.now().millisecondsSinceEpoch;
      recorderSubscription = recorder.onProgress!.listen((e) {
        eventBus.fire(_EvtVoiceVolume(e.decibels!));
      });
      _overlayVoiceVolume.open(context, evtPool: evtPool);
    } catch (e) {
      _overlayVoiceVolume.close();
      cancelRecorderSubscriptions();
      path = null;
      startTimestamp = 0;
      recording = false;
      setState(() {});
    }
  }

  Future<void> stopRecord() async {
    if (!recording) return;
    recording = false;
    setState(() {});
    //结束录音
    try {
      await recorder.stopRecorder();
      endTimestamp = DateTime.now().millisecondsSinceEpoch;
      _overlayVoiceVolume.close();
      cancelRecorderSubscriptions();
      //判断dy 是否发送
      if (dy.abs() > maxDy) {
        //取消发送
      } else {
        //发送
        PgChatRoomStore store = context.read<PgChatRoomStore>();
        if (endTimestamp - startTimestamp < 1500) {
          showToast('说话时间太短了，请重新录制');
          return;
        }
        var res = await gimSdk.gim.sendAudioMsg(toUid: store.remoteUid, msg: AudioMsg(url: path!, dur: endTimestamp - startTimestamp), pushTitle: store.remoteNickname, pushContent: '发来语音消息', placeholder: (msg){
          store.addMsg(msg);
        });
        store.updateMsgByPreId(res!);
      }
    } catch (e) {
      loggerError(e);
    } finally {
      dy = 0;
      path = null;
      startTimestamp = 0;
      endTimestamp = 0;
    }
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onPanDown: (e) {
        startRecord();
      },
      onPanEnd: (e) {
        stopRecord();
      },
      onPanUpdate: (e) {
        setState(() {
          dy += e.delta.dy;
        });
        if (dy.abs() > maxDy) {
          eventBus.fire(_EvtCancel(true));
        } else {
          eventBus.fire(_EvtCancel(false));
        }
      },
      child: Container(
        width: double.infinity,
        height: 41,
        alignment: Alignment.center,
        decoration: BoxDecoration(
            color: recording ? const Color.fromRGBO(34, 34, 34, 0.1) : const Color.fromRGBO(34, 34, 34, 0.05),
            borderRadius: BorderRadius.circular(20)),
        child: text14(recording ? (dy.abs() > maxDy ? '松开 取消' : '松开 结束') : '按住 说话', fontWeight: FontWeight.w600),
      ),
    );
  }
}

class _EvtVoiceVolume {
  final double level;

  _EvtVoiceVolume(this.level);
}

class _EvtCancel {
  final bool cancel;

  _EvtCancel(this.cancel);
}

class _EvtVoiceVolumePool {
  final EventBus eventBus;
  final Set<ValueChanged<_EvtVoiceVolume>> _evtPool = {};
  final Set<ValueChanged<_EvtCancel>> _evtCancelPool = {};

  _EvtVoiceVolumePool(this.eventBus) {
    eventBus.on<_EvtVoiceVolume>().listen((event) {
      if (_evtPool.isEmpty) return;
      for (ValueChanged<_EvtVoiceVolume> fn in _evtPool) {
        fn(event);
      }
    });
    eventBus.on<_EvtCancel>().listen((event) {
      if (_evtCancelPool.isEmpty) return;
      for (ValueChanged<_EvtCancel> fn in _evtCancelPool) {
        fn(event);
      }
    });
  }

  void addEvt(ValueChanged<_EvtVoiceVolume> cb) {
    _evtPool.add(cb);
  }

  void delEvt(ValueChanged<_EvtVoiceVolume> cb) {
    _evtPool.remove(cb);
  }

  void addCancelEvt(ValueChanged<_EvtCancel> cb) {
    _evtCancelPool.add(cb);
  }

  void delCancelEvt(ValueChanged<_EvtCancel> cb) {
    _evtCancelPool.remove(cb);
  }
}

class _VoiceVolume extends StatefulWidget {
  final _EvtVoiceVolumePool evtPool;

  _VoiceVolume({required this.evtPool});

  @override
  State<StatefulWidget> createState() => _VoiceVolumeState();
}

class _VoiceVolumeState extends State<_VoiceVolume> {
  double _dbLevel = 0.0;
  bool cancel = false;

  final double size = windowWidth * 0.4;

  void cb(_EvtVoiceVolume event) {
    setState(() {
      _dbLevel = event.level;
    });
  }

  void cancelEvt(_EvtCancel event) {
    setState(() {
      cancel = event.cancel;
    });
  }

  @override
  void initState() {
    super.initState();
    widget.evtPool.addEvt(cb);
    widget.evtPool.addCancelEvt(cancelEvt);
  }

  @override
  void dispose() {
    widget.evtPool.delEvt(cb);
    widget.evtPool.delCancelEvt(cancelEvt);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    int level = _dbLevel ~/ 10;
    if (level < 0) {
      level = 0;
    } else if (level > 6) {
      level = 6;
    }
    return Positioned(
        left: 0,
        top: windowHeight * 0.5 - windowWidth * 0.2,
        child: Container(
          width: windowWidth,
          height: size,
          alignment: Alignment.center,
          child: Container(
            alignment: Alignment.center,
            width: size,
            height: size,
            decoration:
                BoxDecoration(color: const Color.fromRGBO(0, 0, 0, 0.3), borderRadius: BorderRadius.circular(20)),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Image.asset(cancel ? "icons/rc_voice_volume_cancel.png" : "icons/rc_voice_volume_$level.png",
                    gaplessPlayback: true, width: size * 0.7, height: size * 0.7),
                Material(
                  color: Colors.transparent,
                  child: Container(
                    padding: const EdgeInsets.only(top: 2, bottom: 2, left: 4, right: 4),
                    decoration: BoxDecoration(
                        color: cancel ? const Color(0xFFEB3558) : Colors.transparent,
                        borderRadius: BorderRadius.circular(4)),
                    child: text12(cancel ? '手指松开 取消发送' : '手指上划，取消发送', color: Colors.white),
                  ),
                )
              ],
            ),
          ),
        ));
  }
}

class _OverlayVoiceVolume {
  OverlayEntry? _entry;

  void open(BuildContext context, {required _EvtVoiceVolumePool evtPool}) {
    _entry = OverlayEntry(builder: (BuildContext context) {
      return _VoiceVolume(evtPool: evtPool);
    });
    Overlay.of(context)!.insert(_entry!);
  }

  void close() {
    if (_entry != null) {
      _entry?.remove();
    }
  }
}

_OverlayVoiceVolume _overlayVoiceVolume = _OverlayVoiceVolume();
